package vip.george.realm.chain;

import org.apache.shiro.authc.*;
import org.apache.shiro.realm.AuthenticatingRealm;
import org.apache.shiro.util.ByteSource;
import vip.george.entity.User;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * @author gehaodong
 * @since 2024/3/11
 * <p>
 *     多Realm认证，第二个Realm
 * </P>
 */
public class MyRealmChainTwo extends AuthenticatingRealm {
    private final Map<String, User> userMap = new HashMap<>();

    public MyRealmChainTwo() {
        /**
         * 注意：真实密码是tom123，使用了MD5加密,迭代次数为100，并且加盐了，盐值为: salt
         */
        userMap.put("tom", new User(1, "tom", "b0c95eef4286f8353b919af5c827a814"));
        /**
         * 注意：真实密码是jerry123，使用了MD5加密,迭代次数为100，,并且加盐了，盐值为: salt
         */
        userMap.put("jerry", new User(2, "jerry", "1b441242e4bb5da72b25a32094b5a5a8"));
    }

    /**
     * <p>认证时核心的方法</p>
     * <p>这个方法就干一件事：根据<strong>用户输入的用户名</strong>去数据库查询用户信息并返回</p>
     * <p>至于密码的校验，后续shiro会自动对比</p>
     * @param token 参数是用户登录时输入的用户名和密码等信息
     * @return 返回的是从数据库中查询到的真实用户信息
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
        // 获取用户登陆输入的用户名
        String username = usernamePasswordToken.getUsername();
        User userFromDB = selectUserFromDB(username);
        if (Objects.isNull(userFromDB)) {
            /*
             * 因为是多个realm，这里抛出异常，shiro会自动捕获，这个异常不会抛给前端
             * 具体源码在：org.apache.shiro.authc.pam.ModularRealmAuthenticator#doMultiRealmAuthentication(java.util.Collection, org.apache.shiro.authc.AuthenticationToken)
             */
            throw new UnknownAccountException("多Realm认证时用户名输入错误");
        }
        // 如果通过数据库查询到了用户，就返回给shiro一个封装了用户信息的AuthenticationInfo对象
        return new SimpleAuthenticationInfo(userFromDB, userFromDB.getPassword(), ByteSource.Util.bytes("salt"), getName());
    }


    /**
     * 模拟通过用户名从数据库中查询数据
     * @return
     */
    private User selectUserFromDB(String username) {
        return userMap.get(username);
    }
}
